from errors import * #catch programming errors.

#The node class holds the constructs of the program. It also holds the previous and next node. Why the previous? 
#consider this: 10 0 do ... until
#the do construct can get the 2 previous nodes and make sure they are what they should be, incorperate them into itself, and make itself be in the place of those 2 expressions.
class Node:
 PrintableName="undefined" #helpful for printing compiler errors.
 def __init__(self, prev): #requires the previous node.
  self.prev=prev #store it.
  #add this class to the next pointer of the previous class.
  if prev != None:
   prev.next=self
  self.next=None #no new nodes.
  self.nodes=[] #list of constructs for this node, if holds 2, . holds 1, ...  

 def NextNode(self, node): #store the next node.
  self.next=node

 #Get prior nodes from the tree.
 #***add error checking, rework as recursive func?
 def GetNodes(self, n=2):  
  prior=[] #the list of prior nodes
  tmp=self #pointer to current node.
 #get n nodes. 
  for i in range(n):
   tmp=tmp.prev #move to previous node.
   prior.append(tmp) #add the new node to the list.
  prior.reverse() #we added the expressions in reverse order.
  return prior #give it back.

 #get nodes and confirm type of node. 
 ###Not type(node) but node.__class__ is compared.
 def GetNodesByTypes(self, n, types):
  nodes=self.GetNodes(n) #get prior nodes
  #iterate through types.
  for i in range(n):
   if not isinstance(nodes[i], types[i]): #if the ith node is not an instance of the ith type,
    raise CompilingErrorException("I expected to find type " + types[i].PrintableName + ", but I found " + nodes[i].PrintableName +".") #start the error chain.
  return nodes #All of them check out, return the list.

 #get n nodes, they should all be instances of type.
 def GetNodesByType(self, n, type):
  return self.GetNodesByTypes(n, [type]*n) #create a list of type repeated n times, call GetNodesByTypes.

#Function calls
#basic function call class.
class FunctionCallNode(Node):
 PrintableName="function call"
 def __init__(self, prev, Name, input, output, consume=True):   #takes the name of the function, args in, args out.
  Node.__init__(self, prev) #call super constructor.
  self.Name=Name
  self.input=input #set remaining available args
  self.output=output #set what will be on the stack.
  self.argsInfo=([]+input, []+output) #this is always going to be the list of arguments for the func.
  if consume:
   self.GetPriorArgs(input)

 #consume arguments in prior node
 def GetPriorArgs(self, args):
  #do we have arguments?
  if len(args) > 0:
   #if we are here than arguments remain. If we are at the beginning of the node, there are no more.
   #check to be sure  there is a previous class.
   if not isinstance(self.prev, FunctionCallNode): #if no more,
    raise CompilingErrorException("More arguments were expected but none were to be found.")
   else: #there is at least one more appropriate node before me.
    self.prev.ConsumeArgs(args) #pass args to the previous node.

 #consume expressions or function calls returning expressions.
 def ConsumeArgs(self, args):
  outtst=[]+self.output
  while len(outtst) > 0 and len(args) > 0:
   (a,b) = (outtst.pop(), args.pop())
   if not issubclass(a, b):
    raise CompilingErrorException("I expected to find " + b.PrintableName + " but I found " + a.PrintableName + ".")
  #if len(args) > 0 testid already in below func.
  try:#try to get the rest of the args.
   self.GetPriorArgs(args) #if all argument types match,
   self.output=outtst #record the changes made.
  except CompilingErrorException as e: #oops, types didn't match.
   raise e #pass on the exception but don't record changes, just in case inside the interpreter.   

#expressions, objects, numbers, strings, ...
#Member of FunctionCallNode because an expression by itself calls push to get it on the stack.
class ExpressionNode(FunctionCallNode):
 PrintableName="expression"
 def __init__(self, prev, value, consume=True): #takes the value of the expression.
  FunctionCallNode.__init__(self, prev, "push", [], [self.__class__], consume)	
  self.SetValue(value) #store the value and type.

 def SetValue(self, value):
  self.Value=value  #save the value of the expression.
  self.Type=type(value) #also save the type, we know what we're getting in to.
  self.output=[self.__class__] #set what is on the stack when done.
